﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.

//
// Revision history:
//
// PS - December 2014 - Created this file.
//

using System.Collections.ObjectModel;
using System.Reflection;

namespace System.Linq.Expressions
{
    // PERF: Consider introducing specializations for common patterns of try..catch (possibly with further
    //       specialization for 1 versus many) and try..finally.

    /// <summary>
    /// Lightweight representation of a try/catch/finally/fault block.
    /// </summary>
    public sealed class TryExpressionSlim : ExpressionSlim
    {
        internal TryExpressionSlim(TypeSlim type, ExpressionSlim body, ExpressionSlim @finally, ExpressionSlim fault, ReadOnlyCollection<CatchBlockSlim> handlers)
        {
            Type = type;
            Body = body;
            Handlers = handlers;
            Finally = @finally;
            Fault = fault;
        }

        /// <summary>
        /// Gets the expression node type.
        /// </summary>
        public override ExpressionType NodeType => ExpressionType.Try;

        /// <summary>
        /// Gets the static type of the expression that this <see cref="ExpressionSlim" /> represents.
        /// </summary>
        public TypeSlim Type { get; }

        /// <summary>
        /// Gets the <see cref="ExpressionSlim"/> representing the body of the try block.
        /// </summary>
        public ExpressionSlim Body { get; }

        /// <summary>
        /// Gets the collection of <see cref="CatchBlockSlim"/>s associated with the try block.
        /// </summary>
        public ReadOnlyCollection<CatchBlockSlim> Handlers { get; }

        /// <summary>
        /// Gets the <see cref="ExpressionSlim"/> representing the finally block.
        /// </summary>
        public ExpressionSlim Finally { get; }

        /// <summary>
        /// Gets the <see cref="ExpressionSlim"/> representing the fault block.
        /// </summary>
        public ExpressionSlim Fault { get; }

        /// <summary>
        /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression.
        /// </summary>
        /// <param name="body">The <see cref="Body" /> property of the result.</param>
        /// <param name="handlers">The <see cref="Handlers" /> property of the result.</param>
        /// <param name="finally">The <see cref="Finally" /> property of the result.</param>
        /// <param name="fault">The <see cref="Fault" /> property of the result.</param>
        /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
        public TryExpressionSlim Update(ExpressionSlim body, ReadOnlyCollection<CatchBlockSlim> handlers, ExpressionSlim @finally, ExpressionSlim fault)
        {
            if (body == Body && handlers == Handlers && @finally == Finally && fault == Fault)
            {
                return this;
            }

            return new TryExpressionSlim(Type, body, @finally, fault, handlers);
        }

        /// <summary>
        /// Accepts the expression tree node in the specified visitor.
        /// </summary>
        /// <param name="visitor">Visitor to process the current expression tree node.</param>
        /// <returns>Result of visiting the node.</returns>
        protected internal override ExpressionSlim Accept(ExpressionSlimVisitor visitor)
        {
            return visitor.VisitTry(this);
        }

        /// <summary>
        /// Accepts the expression tree node in the specified visitor.
        /// </summary>
        /// <typeparam name="TExpression">Target type for expressions.</typeparam>
        /// <typeparam name="TLambdaExpression">Target type for lambda expressions. This type has to derive from TExpression.</typeparam>
        /// <typeparam name="TParameterExpression">Target type for parameter expressions. This type has to derive from TExpression.</typeparam>
        /// <typeparam name="TNewExpression">Target type for new expressions. This type has to derive from TExpression.</typeparam>
        /// <typeparam name="TElementInit">Target type for element initializers.</typeparam>
        /// <typeparam name="TMemberBinding">Target type for member bindings.</typeparam>
        /// <typeparam name="TMemberAssignment">Target type for member assignments. This type has to derive from TMemberBinding.</typeparam>
        /// <typeparam name="TMemberListBinding">Target type for member list bindings. This type has to derive from TMemberBinding.</typeparam>
        /// <typeparam name="TMemberMemberBinding">Target type for member member bindings. This type has to derive from TMemberBinding.</typeparam>
        /// <typeparam name="TCatchBlock">Target type for catch blocks.</typeparam>
        /// <typeparam name="TSwitchCase">Target type for switch cases.</typeparam>
        /// <typeparam name="TLabelTarget">Target type for label targets.</typeparam>
        /// <param name="visitor">Visitor to process the current expression tree node.</param>
        /// <returns>Result of visiting the node.</returns>
        protected internal override TExpression Accept<TExpression, TLambdaExpression, TParameterExpression, TNewExpression, TElementInit, TMemberBinding, TMemberAssignment, TMemberListBinding, TMemberMemberBinding, TCatchBlock, TSwitchCase, TLabelTarget>(ExpressionSlimVisitor<TExpression, TLambdaExpression, TParameterExpression, TNewExpression, TElementInit, TMemberBinding, TMemberAssignment, TMemberListBinding, TMemberMemberBinding, TCatchBlock, TSwitchCase, TLabelTarget> visitor)
        {
            return visitor.VisitTry(this);
        }
    }
}
